package net.sf.ivyide;

import com.intellij.openapi.diagnostic.Logger;
import org.apache.tools.ant.Project;

import java.io.File;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AntContentRoot implements ContentRoot {

    private static final Logger LOGGER = Logger.getInstance(AntContentRoot.class.getName());

    private File m_rootDir;
    private Project m_project;
    private ContentRootTemplate m_template;
    private static final Pattern ANT_VARIABLE_PATTERN = Pattern.compile("\\$\\{([^\\{]*)\\}");

    public AntContentRoot(ContentRootTemplate template, Project project) {
        m_template = template;
        m_project = project;
        m_rootDir = getFileFromTemplate(template.getRootDirTemplate(), project, true);
    }

    public static File getFileFromTemplate(String rootDirTemplate, Project project, boolean checkExisting) {
        File[] filesFromTemplate = getFilesFromTemplate(rootDirTemplate, project, checkExisting);
        if (filesFromTemplate.length != 1) {
            return null;
        }
        return filesFromTemplate[0];
    }

    public static File[] getFilesFromTemplate(String rootDirTemplate, Project project, boolean checkExistence) {
        if (rootDirTemplate == null) {
            return new File[0];
        }
        Matcher matcher = ANT_VARIABLE_PATTERN.matcher(rootDirTemplate);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String propertyName = matcher.group();
            String value = getProperty(propertyName.substring(2, propertyName.length() - 1), project);
            if (value == null) {
                System.out.println("Unknown property: " + propertyName);
                propertyName = propertyName.replaceAll("\\$", "\\\\\\$");
                matcher.appendReplacement(sb, propertyName);
            } else {
                //escape \
                value = value.replaceAll("\\\\", "\\\\\\\\");
                matcher.appendReplacement(sb, value);
            }
        }
        matcher.appendTail(sb);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Resolved " + rootDirTemplate + " to " + sb.toString());
        }
        String fileNames = sb.toString();
        String[] strings = fileNames.split(",");

        List<File> files = new LinkedList<File>();
        for (String argument : strings) {
            File file = new File(argument);
            if (!checkExistence || file.exists()) {
                files.add(file);
            }
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Returning " + files);
        }
        return files.toArray(new File[files.size()]);
    }

    public File getDir() {
        return m_rootDir;
    }

    public File[] getExcludedDirs() {
        Set<File> subDirsToKeep = new HashSet<File>();
        addRootSubDir(subDirsToKeep, getMainJavaDirs());
        addRootSubDir(subDirsToKeep, getMainResourceDirs());
        addRootSubDir(subDirsToKeep, getTestJavaDirs());
        addRootSubDir(subDirsToKeep, getTestResourceDirs());

        File[] files = m_rootDir.listFiles();
        if (files == null) {
            throw new RuntimeException(String.valueOf(m_rootDir));
        }
        LinkedList<File> subDirs = new LinkedList<File>(Arrays.asList(files));
        Iterator<File> subDirsIterator = subDirs.iterator();
        while (subDirsIterator.hasNext()) {
            File file = subDirsIterator.next();
            if (!file.isDirectory()) {
                subDirsIterator.remove();
            } else {
                if (subDirsToKeep.contains(file)) {
                    subDirsIterator.remove();
                } else {
                    if (m_template.getIncludedSubdirsPattern() != null) {
                        String name = file.getName();
                        Matcher matcher = m_template.getIncludedSubdirsPattern().matcher(name);
                        if (matcher.matches()) {
                            subDirsIterator.remove();
                        }
                    }
                }
            }
        }
        return subDirs.toArray(new File[subDirs.size()]);
    }

    private void addRootSubDir(Set<File> subDirsToKeep, File[] mainJavaDirs) {
        for (File dir : mainJavaDirs) {
            while (dir.getParentFile() != null) {
                if (dir.getParentFile().equals(m_rootDir)) {
                    subDirsToKeep.add(dir);
                    break;
                } else {
                    dir = dir.getParentFile();
                }
            }
        }
    }

    File[] createFileArray(String... templates) {
        List<File> files = new LinkedList<File>();
        for (String template : templates) {
            files.addAll(Arrays.asList(getFilesFromTemplate(template, m_project, true)));
        }
        return files.toArray(new File[files.size()]);
    }

    private static String getProperty(String propName, Project project) {
        //System.out.println(getDir() + " property = " + propName + "  value = " + value);
        return project.getProperty(propName);
    }

    public File[] getMainJavaDirs() {
        return createFileArray(m_template.getMainJavaDirTemplate());
    }

    public File[] getTestJavaDirs() {
        return createFileArray(m_template.getTestJavaDirTemplate());
    }

    public File[] getMainResourceDirs() {
        return createFileArray(m_template.getMainResourceDirTemplate());
    }

    public File[] getTestResourceDirs() {
        return createFileArray(m_template.getTestResourceDirTemplate());
    }

}
